/*
    Space Rocks! Avoid the rocks as long as you can!
    Copyright (C) 2001  Paul Holt <pcholt@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
#define EDITION " - The BLUBATS edition"
extern int font_height;

// includes
#include <SDL/SDL_image.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <signal.h>

#include "config.h"
#include "SFont.h"
#include "sound.h"
#include "random_gen.h"
#include "bangdots.h"
#include "blubats.h"
#include "display_subsystem.h"
#include "engine_exhaust.h"
#include "greeblies.h"
#include "highscore_io.h"
#include "input_functions.h"
#include "intro.h"
#include "laser.h"
#include "mood_item.h"
#include "powerup.h"
#include "rockdodger_globals.h"
#include "rocks.h"
#include "ship.h"
#include "datafun.h"

// constants
#define MAXROCKHEIGHT 100
#define M 255
#define BIG_FONT_FILE "20P_Betadance.png"

// macros
#define COND_ERROR(a) if ((a)) {initerror=strdup(SDL_GetError());return 1;}
#define NULL_ERROR(a) COND_ERROR((a)==NULL)


void handlesignal(int signal) {
  SDL_Quit();
  exit(128 + signal);
}

// SDL_Surface global variables
SDL_Surface *surf_t_game;	// Title element "game"
SDL_Surface *surf_t_over;	// Title element "over"
SDL_Surface *surf_t_paused;	// Title element "paused"
SDL_Surface *surf_font_big;	// The big font
SDL_Surface *surf_gauge_shield[2];	// The shield gauge 0=on,1=off
SDL_Surface *surf_gauge_laser[2];	// The laser gauge 0=on,1=off

Scroller_t *highscore_scroller = NULL; // Scroller for highscore

// Other global variables
char topline[128];
char *initerror = "";

float rockrate, rockspeed;

int nships;
float countdown = 0;
int laser = 0;
int shieldsup = 0;
int oss_sound_flag = 0;
float faderate;

char *sequence[] = {
  "Rock Dodger",
  "Press SPACE to start!",
  "http://code.google.com/p/rockdodger/",
  "Arrow keys move the ship.",
  "Press S for shield and D for laser.",
  "If you need a pause press P.",
  "Pressing F1 gets you to the setup screen.",
  "You can bounce off the sides of the screen.",
  "Caution, do not try this in real life. Space has no sides.",
  "",
  "If you have a <500Mhz machine -- buy a new one!",
  "",
  "The little green guys are called Greeblies.",
  "Later in the game you will meet their friends...",
  "...the very dangerous Blubats!",
  "Watch out! Blubats may leave some droppings!",
  "Blubat droppings should not be inhaled!",
  "",
  "Code: RPK, Paul Holt [pcholt at gmail]",
  "GfX: RPK, Paul Holt [pcholt at gmail]",
  "Music: Jack Beatmaster, zake, Strobo, Roz",
  "",
  "The engine on the back of the ship provides no thrust.",
  "It's a sparkler, there for effect.",
  ""
};
const int nsequence = sizeof(sequence) / sizeof(char *);

extern char *optarg;
extern int optind, opterr, optopt;

unsigned short screenshot_number = 0; /*!< Count the number of screen
                                         shots. This makes is possible
                                         to produce multiple
                                         screenshots per run.
				       */

// ************************************* FUNCS

int restart_game();

void drawlaser() {
  int i, xc, hitrock, hitgreeb;

  if(laserlevel < 0)
    return;
  laserlevel -= movementrate;
  if(laserlevel < 0) {
    // If the laser runs out completely, there will be a delay before it can be brought back up
    laserlevel = -W / 2;
    return;
  }

  hitrock = -1;
  hitgreeb = -1;
  xc = xsize;
  // let xc = x coordinate of the collision between the laser and a space rock
  // 1. Calculate xc and determine the asteroid that was hit
  for(i = 0; i < MAX_ROCKS; i++) {
    if(rock[i].active) {
      SDL_Surface *rocksurf = get_rock_surface(i, 0);
      if(yship + 12 > rock[i].y && yship + 12 < rock[i].y + rocksurf->h
	 && xship + 32 < rock[i].x + (rocksurf->w / 2)
	 && rock[i].x + (rocksurf->w / 2) < xc) {
	xc = rock[i].x + (rocksurf->w / 2);
	hitrock = i;
      }
    }
  }
  for(i = 0; i < MAX_GREEBLES; i++) {
    int greebheight = 16;
    int greebwidth = 16;
    struct greeble *g;
    g = &greeb[i];

    if(g->active) {
      int greebx = (int) g->x;
      int greeby = (int) g->y;
      if(g->landed) {
	greebx += rock[g->target_rock_number].x;
	greeby += rock[g->target_rock_number].y;
      }
      if(yship + 12 > greeby && yship + 12 < greeby + greebheight &&
	 xship + 32 < greebx + (greebwidth / 2) && greebx + (greebwidth / 2) <
	 xc) {
	xc = greebx + (greebwidth / 2);
	hitgreeb = i;
      }
    }

  }

  if(hitrock >= 0)
    heat_rock_up(hitrock, movementrate);
  if(hitgreeb >= 0)
    kill_greeb(hitgreeb);

  if((hitgreeb = check_blubat_hit((int) yship + 12, xc)) >= 0) {
    kill_blubat(hitgreeb);
  }
  // Plot a number of random dots between xship and xsize (or xc as in x-collision)
  draw_random_dots(surf_screen, yship + 12, xship + 32, xc);
}

/*! \brief Set the SDL video mode
 *
 * This function sets the video mode and surf_screen. It will use the
 * fullscreen flag to decide if it is a fullscreen surface or not.
 *
 * \return the new surf_screen
 */
SDL_Surface *set_video_mode() {
  Uint32 flag;

  // Attempt to get the required video size
  flag = SDL_DOUBLEBUF | SDL_HWSURFACE;
  if(opt_fullscreen)
    flag |= SDL_FULLSCREEN;
  surf_screen = SDL_SetVideoMode(xsize, ysize, RD_VIDEO_BPP, flag);
  return surf_screen;
}


int init() {
  int i;
  SDL_Surface *temp;

  signal(SIGINT, handlesignal);
  signal(SIGTERM, handlesignal);
  signal(SIGQUIT, handlesignal);

  read_high_score_table();

  data_dir = get_data_dir();

  if(oss_sound_flag) {

    // Initialise SDL with audio and video
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
      oss_sound_flag = 0;
      printf("Can't open sound, starting without it\n");
      atexit(SDL_Quit);
    } else {
      atexit(SDL_Quit);
      atexit(SDL_CloseAudio);
      oss_sound_flag = init_sound();
    }

  } else {
    // Initialise with video only
    COND_ERROR(SDL_Init(SDL_INIT_VIDEO) != 0);
    atexit(SDL_Quit);
  }

  // Attempt to initialize a joystick
  if(joystick_flag) {
    if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) {
      joystick_flag = 0;
      printf("Can't initialize joystick subsystem, starting without it.\n");
    } else {
      int njoys;
      njoys = SDL_NumJoysticks();
      printf("%d joystick(s) detected\n", njoys);
      if(njoys == 0) {
	printf
	  ("That's not enough joysticks to start with joystick support\n");
	joystick_flag = 0;
      } else {
	joysticks[0] = SDL_JoystickOpen(0);
	if(joysticks[0] == NULL) {
	  printf("Couldn't open joystick %d\n", 0);
	  joystick_flag = 0;
	}
      }
    }
  }

  // Attempt to get the required video size
  set_video_mode();
  NULL_ERROR(surf_screen);

  //TODO: Add a splash screen...
  //SDL_FillRect(surf_screen, NULL, SDL_MapRGB(surf_screen->format, 0xff, 0, 0));
  //SDL_Flip(surf_screen);

  // Set the title bar text
  SDL_WM_SetCaption("Rock Dodgers", "rockdodgers");

  // Set the heat color from the range 0 (cold) to 300 (blue-white)
  for(i = 0; i < W * 3; i++)
    heatcolor[i] = SDL_MapRGB(surf_screen->format,
			      (i < W) ? (i * M / W) : (M),
			      (i < W) ? 0 : (i <
					     2 * W) ? ((i - W) * M / W) : M,
			      (i < 2 * W) ? 0 : ((i - W) * M / W)
			      // Got that?
      );

  // Load global font
  surf_font_big = IMG_Load(load_file(BIG_FONT_FILE));
  InitFont(surf_font_big);

  // Load the Title elements
  NULL_ERROR(surf_t_rock = load_image("rock.png", 255, 0, 0));
  NULL_ERROR(surf_t_dodger = load_image("dodgers.png", 255, 0, 0));

  NULL_ERROR(surf_t_game = load_image("game.png", 255, 0, 0));
  NULL_ERROR(surf_t_over = load_image("over.png", 255, 0, 0));
  NULL_ERROR(surf_t_paused = IMG_Load(load_file("paused.png")));

  NULL_ERROR(surf_gauge_laser[0] = IMG_Load(load_file("laser0.png")));
  NULL_ERROR(surf_gauge_laser[1] = IMG_Load(load_file("laser1.png")));
  NULL_ERROR(surf_gauge_shield[0] = IMG_Load(load_file("shield0.png")));
  NULL_ERROR(surf_gauge_shield[1] = IMG_Load(load_file("shield1.png")));


  // Load the spaceship surface from the spaceship file
  temp = SDL_LoadBMP(load_file("ship.bmp"));
  NULL_ERROR(temp);
  SDL_SetColorKey(temp, SDL_SRCCOLORKEY | SDL_RLEACCEL,
		  (RD_VIDEO_TYPE) SDL_MapRGB(temp->format, 0, 255, 0));
  surf_ship = SDL_DisplayFormat(temp);
  NULL_ERROR(surf_ship);

  // Load the little spaceship surface from the spaceship file
  temp = SDL_LoadBMP(load_file("ship_small.bmp"));
  NULL_ERROR(temp);
  SDL_SetColorKey(temp, SDL_SRCCOLORKEY | SDL_RLEACCEL,
		  (RD_VIDEO_TYPE) SDL_MapRGB(temp->format, 0, 255, 0));
  surf_small_ship = SDL_DisplayFormat(temp);
  NULL_ERROR(surf_small_ship);

  NULL_ERROR(init_ship());
  init_engine_dots();
  NULL_ERROR(init_blubats());
  NULL_ERROR(init_powerup());
  NULL_ERROR(init_mood_item());
  // Load all our lovely rocks
  NULL_ERROR(init_rocks());
  NULL_ERROR(init_greeblies());

  // Remove the mouse cursor
#ifdef SDL_DISABLE
  SDL_ShowCursor(SDL_DISABLE);
#endif

  return 0;
}


void showgauge(int x, SDL_Surface * surf[2], float fraction) {
  static int endx, w;
  static SDL_Rect src, dest;
  src.y = 0;
  if(fraction > 0) {
    if(fraction > 1)
      fraction = 1.0;
    src.x = 0;
    w = src.w = surf[0]->w * fraction;
    src.h = surf[0]->h;
    endx = src.w;
    dest.w = src.w;
    dest.h = src.h;
    dest.x = x;
    dest.y = ysize - src.h - 10;
    SDL_BlitSurface(surf[0], &src, surf_screen, &dest);
  } else {
    endx = 0;
    w = 0;
  }

  src.x = endx;
  src.w = surf[1]->w - w;
  src.h = surf[1]->h;
  dest.w = src.w;
  dest.h = src.h;
  dest.x = x + endx;
  dest.y = ysize - src.h - 10;
  SDL_BlitSurface(surf[1], &src, surf_screen, &dest);
}


void display_version_n_text() {
  char *text = "Version " VERSION EDITION "  (" COMPILEDATE ")";
  int x = (xsize - SFont_wide(text)) / 2 + sinf(fadetimer / 4.5) * 10;
  PutString(surf_screen, x, ysize - 50 + sinf(fadetimer / 2) * 5, text);

  text = sequence[(int) (fadetimer / 40) % nsequence];
  x = (xsize - SFont_wide(text)) / 2 + cosf(fadetimer / 4.5) * 10;
  PutString(surf_screen, x, ysize - 100 + cosf(fadetimer / 3) * 5, text);
  fadetimer += movementrate / 2.0;
}


/*! \brief Display de list o high scores mon.
 */
void display_highscores() {
  unsigned int i;
  char s[128];

  PutString(surf_screen, 180, 50, "High scores");
  for(i = 0; i < MAXIMUM_HIGH_SCORE_ENTRIES; ++i) {
    sprintf(s, "#%1d", i + 1);
    PutString(surf_screen, 150, 50 + (i + 2) * font_height, s);
    sprintf(s, "%4ld", high[i].score);
    PutString(surf_screen, 200, 50 + (i + 2) * font_height, s);
    sprintf(s, "%3s", high[i].name);
    PutString(surf_screen, 330, 50 + (i + 2) * font_height, s);
  }
}


/*! \brief Enter the highscore
 *
 * Called once per frame from draw() if we are in highscore enter
 * mode.
 */
void enter_highscore(void) {
  assert(state == HIGH_SCORE_ENTRY);
  if(scorerank >= 0) {
    play_tune(2);
    if(SFont_Input
       (surf_screen, 330, 50 + (scorerank + 2) * font_height, 300, name_input_buf)) {
      // Insert new high score and name into high score table
      high[scorerank].score = score;
      high[scorerank].name = strdup(name_input_buf);
      high[scorerank].allocated = 1;
      scorerank = -1;		//we are unworthy again

      // Set the global name string to "", ready for the next winner
      name_input_buf[0] = 0;

      // Change state to briefly show high scores page
      state = HIGH_SCORE_DISPLAY;
      state_timeout = 200;

      // Write the high score table to the file
      write_high_score_table();

      // Play the title page tune
      play_tune(0);
    }
  } else {
    state = HIGH_SCORE_DISPLAY;
    state_timeout = 400;
    play_tune(0);
  }
}


void draw_titlepage(void) {
  draw_ghostly_rock_dodger(surf_t_rock, surf_t_dodger);
  display_version_n_text();
}


void draw_gameover(void) {
  SDL_Rect src, dest;
  float fadegame, fadeover;

  if(fadetimer < 3.0 / faderate)
    fadegame = fadetimer / (3.0 / faderate);
  else
    fadegame = 1.0;

  if(fadetimer < 3.0 / faderate)
    fadeover = 0.0;
  else if(fadetimer < 6.0 / faderate)
    fadeover = ((3.0 / faderate) - fadetimer) / (6.0 / faderate);
  else
    fadeover = 1.0;

  src.w = surf_t_game->w;
  src.h = surf_t_game->h;
  src.x = 0;
  src.y = 0;
  dest.w = src.w;
  dest.h = src.h;
  dest.x = (xsize - src.w) / 2;
  dest.y = (ysize - src.h) / 2 - 40;
  SDL_SetAlpha(surf_t_game, SDL_SRCALPHA,
	       (int) (fadegame *
		      (200 + 55 * cosf(fadetimer += movementrate / 1.0))));
  SDL_BlitSurface(surf_t_game, &src, surf_screen, &dest);

  src.w = surf_t_over->w;
  src.h = surf_t_over->h;
  dest.w = src.w;
  dest.h = src.h;
  dest.x = (xsize - src.w) / 2;
  dest.y = (ysize - src.h) / 2 + 40;
  SDL_SetAlpha(surf_t_over, SDL_SRCALPHA,
	       (int) (fadeover * (200 + 55 * sinf(fadetimer))));
  SDL_BlitSurface(surf_t_over, &src, surf_screen, &dest);
}


/*! \brief Draw all
 *
 * Draw all the objects on the standard surface.
 *
 * \return bang=0 if everything ok, bang=1 if ship was hit.
 */ 
int draw() {
  int i;
  int bang;

  bang = 0;
  draw_background_objects();
  // If it's firing, draw the laser
  if(laser) {
    drawlaser();
  } else {
    if(laserlevel < 3 * W) {
      laserlevel += movementrate / 4;
    }
  }

  // Draw ship
  if(state == GAME_PLAY || state == GAME_PAUSED || state == DEMO) {
    draw_ship(surf_screen);
  }

  // Draw all the rocks, in all states. 
  display_rocks(surf_screen);
  // Draw all blubats
  display_blubats(surf_screen);

  switch (state) {
  case GAME_OVER:
    // If it's game over, show the game over graphic in the dead centre
    draw_gameover();
    break;
  case RESTART_GAME:
  case TITLE_PAGE:
    draw_titlepage();
    break;
  case HIGH_SCORE_ENTRY:
    enter_highscore();
    update_and_draw_scroller(highscore_scroller, surf_screen, movementrate);
    display_highscores();
    break;
  case HIGH_SCORE_DISPLAY:
    display_highscores();
    display_version_n_text();
    break;
  case GAME_PAUSED:
    {
      SDL_Rect dest;
      dest.w = surf_t_paused->w;
      dest.h = surf_t_paused->h;
      dest.x = (xsize - dest.w) / 2;
      dest.y = (ysize - dest.h) / 2;
      SDL_BlitSurface(surf_t_paused, NULL, surf_screen, &dest);
    }
    break;
  case DEAD_PAUSE:
  case GAME_PLAY:
    break;
  case SETUP:
    //Not done here!
    assert(0);
  case DEMO:
  case QUIT_GAME:
    break;			// handled below
  }

  if(state == GAME_PLAY) {
    // Show the freaky shields
    if((initialshield > 0) || (shieldsup && shieldlevel > 0)) {
      draw_ship_shield(surf_screen);
    } else {
      // When the shields are off, check that the black points 
      // on the ship are still black, and not covered up by rocks
      bang = ship_check_collision(surf_screen);
    }

    //If in game display the laserpowerup
    if(powerup.active) {
      display_powerup(surf_screen);
      //Yes, shields protect against *everything*!
      if(!((initialshield > 0) || (shieldsup && shieldlevel > 0))) {
	int bangpowerup = ship_check_collision(surf_screen);
	if(bangpowerup) {
	  switch(powerup.active) {
	  case POWERUP_LASER:
	    laserlevel += 16;
	    if(laserlevel > 3 * W)
	      laserlevel = 3 * W;
	    break;
	  case POWERUP_SHIELD:
	    shieldlevel += 13;
	    if(shieldlevel > 3 * W)
	      shieldlevel = 3 * W;
	    break;
	  case POWERUP_NONE:
	    puts("Illegal powerup (NONE?)!");
	    play_sound(4);
	    break;
	  }
	  deactivate_powerup();
	  play_sound(5);
	}
      }
    }
  }

  // Draw all the little ships
  if(state == GAME_PLAY || state == DEAD_PAUSE || state == GAME_OVER) {
    draw_little_ships(nships, surf_screen);
    // Show the shield gauge
    showgauge(10, surf_gauge_shield, shieldlevel / (3 * W));
    // Show the laser gauge
    showgauge(200, surf_gauge_laser, laserlevel / (3 * W));
  }

#ifdef DEBUG
  //printf("last_ticks=%08X ticks_since_last=%02X movementrate=%10.4e\n", last_ticks, ticks_since_last, movementrate);
  char tempbuf[80];
  sprintf(tempbuf, "state_timeout=%g", state_timeout);
  PutString(surf_screen, 0, 0, tempbuf);
#endif

  if(state == GAME_PLAY)
    inc_score(0, 0, SCORE_INCREMENT_FACTOR);	//This many points per screen update
  // Always draw the last score
  i = xsize - 250;
  snprintf(topline, sizeof(topline), "Score: %ld", score);
  PutString(surf_screen, i, 30, topline);
  snprintf(topline, sizeof(topline), "Level: %d", (int) level);
  PutString(surf_screen, i, 50, topline);

  // Update the surface
  SDL_Flip(surf_screen);
  return bang;
}


/*! \brief called if ship was hit
 */
void crash_boom_bang() {
  if(oss_sound_flag) {
    // Play the explosion sound
    play_sound(0);
  }
  makebangdots(xship, yship, xvel, yvel, surf_ship, 30, 3);
  if(--nships <= 0) {
    state = GAME_OVER;
    state_timeout = 200.0;
    fadetimer = 0.0;
    faderate = movementrate;
    level = floorf(level); /*Otherwise if level is >x.98 and
			    smaller then x+1 the function
			    draw_infinite_black() will cause
			    problems. It will loop the sound and
			    will not black the background.*/
    //Remove blubats
    deactivate_all_blubats();
  } else {
    state = DEAD_PAUSE;
    state_timeout = 120.0;
  }
}


/*

  Subsystems [Loki Games, Programming Linux Games, No Starch Press, p 15, 2001]:
Keyboard
Mouse			Input Subsystem
Game Pad
			Network Subsystem
LAN or
Internet
			Update Subsystem
3D Hardware /
Framebuffer
			Display Subsystem
			Audio Subsystem
Sound Card
Figure 1–1: A typical game loop

 */

void ready_for_takeoff() {
  int i;
  for(i = 0; i < MAX_ROCKS; i++)
    rock[i].active = 0;
  deactivate_greeblies();

  rockspeed = 5.0 * xsize / 640;
  rockrate = 0;
  countdown = 1.111;

  nships = 4;
  clear_score();
  level = 1;

  state = GAME_PLAY;
  play_tune(1);

  xvel = -1;
  yvel = 0;
  xship = 0;
  yship = ysize / 2;
  shieldlevel = 3 * W;
  laserlevel = 3 * W;
  initialshield = 150;

  play_sound(4);
}


/*! \brief game loop called once per frame
 *
 * This is the game loop where all the magic happens. This loop is
 * only left if state == QUIT_GAME or RESTART_GAME.
 *
 * \return 0 == quit the game, 1 == restart game
 */
int gameloop() {
  inputstate_t inputstate;
  Uint8 *keystate;

  while(state != QUIT_GAME && state != RESTART_GAME) {
    update_movement_rate();
    keystate = input_subsystem(&inputstate);
    handle_state_timeout();

    if(state != GAME_PAUSED) {
      rockspeed = 4.885 * xsize / 640.0 + sqrtf(floorf(level)) / 5.0;
      rockrate = 0.13182 + sqrtf(floorf(level)) / 20.0;
      if(level - (int) level < 0.8) {
	if(state != GAME_PLAY)
	  countdown -= 0.3 * movementrate;
	else
	  countdown -= rockrate * movementrate;
	
	while(countdown < 0) {
	  countdown++;
	  create_rock(rockspeed);
	}
      }
    }

    switch(state) {
    case TITLE_PAGE:
      // Before the rocks are drawn, the greeblies are shown playing amongst them.
      activate_one_greeblie();
      // Move all the rocks
      update_rocks();
      // Move the powerup
      update_powerup();
      move_all_greeblies();
      update_bangdots(surf_screen);
      break;
    case DEAD_PAUSE:
    case GAME_PLAY:
      // Increase difficulty
      level += movementrate / 250;
      //Activate a blubat if necessary
      if(rnd() < (level - 9) / 777 && ((int)level) % 5 != 0) { //Every 5th level no blubats
	activate_one_blubat();
      }
      //Update greeblies
      if(rnd() < .02 * level * movementrate) {
	activate_one_greeblie();
      }
      // Move all the rocks
      update_rocks();
      // Move ship to new position
      ship_update_position();
      // Move the powerup
      update_powerup();
      move_all_greeblies();
      //Update blubats
      move_all_blubats();
      update_bangdots(surf_screen);
      break;
    case GAME_OVER:
    case HIGH_SCORE_DISPLAY:
    case HIGH_SCORE_ENTRY:
      // Move all the rocks
      update_rocks();
      // Move ship to new position
      ship_update_position();
      // Move the powerup
      update_powerup();
      move_all_greeblies();
      //Update blubats
      move_all_blubats();
      update_bangdots(surf_screen);
      break;
    case SETUP:
      /*
       * The ignore is necessary so that we do not switch back and
       * forth between the two. It can be removed later on(?).
       */
      temporary_disable_key_input();
      setuploop();
      temporary_disable_key_input();
      break;
    case RESTART_GAME:
    case GAME_PAUSED:
    case DEMO:
    case QUIT_GAME:
      break;
    }

    //Was the ship hit?
    if(draw() && state == GAME_PLAY) crash_boom_bang();

    if(keystate[SDLK_SPACE]
       && (state == HIGH_SCORE_DISPLAY || state == TITLE_PAGE
	   || state == DEMO)) {
      ready_for_takeoff();
    }

    laser = 0;
    if(state == GAME_PLAY) {
      get_joystick_input(&inputstate);
      if(inputstate.inputstate[UP]) {
	yvel -= 1.5 * movementrate;
	create_engine_dots2(5, 3); // Create engine dots out the side we're moving from
      }
      if(inputstate.inputstate[DOWN]) {
	yvel += 1.5 * movementrate;
	create_engine_dots2(5, 1);
      }
      if(inputstate.inputstate[LEFT]) {
	xvel -= 1.5 * movementrate;
	create_engine_dots2(5, 2);
      }
      if(inputstate.inputstate[RIGHT]) {
	xvel += 1.5 * movementrate;
	create_engine_dots2(5, 0);
      }
      // Create more engine dots comin out da back
      create_engine_dots(250);
      if(inputstate.inputstate[LASER]) {
	laser = 1;
      }
      if(inputstate.inputstate[FAST]) {
	fast = 1;
      }
      if(inputstate.inputstate[NOFAST]) {
	fast = 0;
      }
      shieldsup = inputstate.inputstate[SHIELD];
    }
    //Check for screen shot.
    if(state != HIGH_SCORE_ENTRY && inputstate.inputstate[SCREENSHOT]) {
      int ret;
      char buf[21];
      
      sprintf(buf, "snapshot.%02hX.bmp", screenshot_number++);
      ret = SDL_SaveBMP(surf_screen, buf);
      fprintf(stderr, "Screenshot '%s' saved %s.\n", buf, ret == 0 ? "successfully" : "unsuccessfully(!)");
    }

    // DEBUG mode to slow down the action, and see if this game is playable on a 486
    if(fast)
      SDL_Delay(100);
  }
  return state == RESTART_GAME ? 1 : 0;
}


unsigned char scrap_writer_iii_17_64c[] = {
  0x00, 0x20, 0x3e, 0x41, 0x5d, 0x51, 0x5d, 0x41, 0x3e, 0x00, 0x3f, 0x21,
  0x21, 0x7f, 0x61, 0x61, 0x61, 0x00, 0x7e, 0x42, 0x42, 0x7f, 0x61, 0x61,
  0x7f, 0x00, 0x3f, 0x21, 0x20, 0x60, 0x60, 0x61, 0x7f, 0x00, 0x3f, 0x21,
  0x21, 0x61, 0x61, 0x61, 0x7f, 0x00, 0x3f, 0x20, 0x20, 0x7c, 0x60, 0x60,
  0x7f, 0x00, 0x3f, 0x20, 0x20, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x3f, 0x21,
  0x20, 0x67, 0x61, 0x61, 0x7f, 0x00, 0x21, 0x21, 0x21, 0x7f, 0x61, 0x61,
  0x61, 0x00, 0x04, 0x04, 0x04, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x01, 0x01,
  0x01, 0x03, 0x43, 0x43, 0x7f, 0x00, 0x21, 0x23, 0x26, 0x7c, 0x66, 0x63,
  0x61, 0x00, 0x20, 0x20, 0x20, 0x60, 0x60, 0x60, 0x7f, 0x00, 0x7f, 0x49,
  0x49, 0x69, 0x61, 0x61, 0x61, 0x00, 0x21, 0x31, 0x39, 0x6d, 0x67, 0x63,
  0x61, 0x00, 0x3f, 0x23, 0x23, 0x61, 0x61, 0x61, 0x7f, 0x00, 0x3f, 0x21,
  0x21, 0x7f, 0x60, 0x60, 0x60, 0x00, 0x3f, 0x23, 0x23, 0x61, 0x61, 0x62,
  0x7d, 0x00, 0x7e, 0x42, 0x42, 0x7f, 0x61, 0x61, 0x61, 0x00, 0x7f, 0x41,
  0x40, 0x7f, 0x03, 0x43, 0x7f, 0x00, 0x7f, 0x08, 0x08, 0x18, 0x18, 0x18,
  0x18, 0x00, 0x21, 0x21, 0x21, 0x61, 0x61, 0x61, 0x7f, 0x00, 0x61, 0x61,
  0x61, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x21, 0x21, 0x21, 0x61, 0x6d, 0x7f,
  0x73, 0x00, 0x41, 0x23, 0x16, 0x1c, 0x34, 0x62, 0x41, 0x00, 0x61, 0x61,
  0x61, 0x7f, 0x01, 0x01, 0x7f, 0x00, 0x7f, 0x03, 0x03, 0x1c, 0x60, 0x60,
  0x7f, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x0c, 0x12,
  0x30, 0x7c, 0x30, 0x62, 0xfc, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
  0x3c, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x10,
  0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x08, 0x08, 0x08, 0x18, 0x18, 0x00, 0x18, 0x00, 0x63, 0x63,
  0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f,
  0x36, 0x00, 0x08, 0x7f, 0x68, 0x7f, 0x0b, 0x7f, 0x08, 0x00, 0x61, 0x61,
  0x02, 0x1c, 0x20, 0x43, 0x43, 0x00, 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66,
  0x3f, 0x00, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18,
  0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18,
  0x30, 0x00, 0x08, 0x2a, 0x1c, 0x7f, 0x1c, 0x2a, 0x08, 0x00, 0x08, 0x08,
  0x08, 0x7f, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
  0x18, 0x38, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30,
  0x60, 0x00, 0x7f, 0x7f, 0x63, 0x63, 0x63, 0x7f, 0x7f, 0x00, 0x0c, 0x0c,
  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x7f, 0x7f, 0x03, 0x7f, 0x60, 0x7f,
  0x7f, 0x00, 0x7f, 0x7f, 0x03, 0x0f, 0x03, 0x7f, 0x7f, 0x00, 0x63, 0x63,
  0x63, 0x7f, 0x03, 0x03, 0x03, 0x00, 0x7f, 0x7f, 0x60, 0x7f, 0x03, 0x7f,
  0x7f, 0x00, 0x7f, 0x7f, 0x60, 0x7f, 0x63, 0x7f, 0x7f, 0x00, 0x7f, 0x7f,
  0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x7f, 0x7f, 0x63, 0x7f, 0x63, 0x7f,
  0x7f, 0x00, 0x7f, 0x7f, 0x63, 0x7f, 0x03, 0x7f, 0x7f, 0x00, 0x00, 0x00,
  0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18,
  0x18, 0x38, 0x0e, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0e, 0x00, 0x00, 0x7f,
  0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18,
  0x70, 0x00, 0x7f, 0x63, 0x03, 0x1f, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
  0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x41, 0x7f, 0x41,
  0x41, 0x00, 0x00, 0x00, 0x7e, 0x42, 0x7f, 0x41, 0x7f, 0x00, 0x00, 0x00,
  0x7f, 0x40, 0x40, 0x40, 0x7f, 0x00, 0x00, 0x00, 0x7e, 0x41, 0x41, 0x41,
  0x7e, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x78, 0x40, 0x7f, 0x00, 0x00, 0x00,
  0x7f, 0x40, 0x78, 0x40, 0x40, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x4f, 0x41,
  0x7f, 0x00, 0x00, 0x00, 0x41, 0x41, 0x7f, 0x41, 0x41, 0x00, 0x00, 0x00,
  0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x41,
  0x7f, 0x00, 0x00, 0x00, 0x41, 0x42, 0x7c, 0x42, 0x41, 0x00, 0x00, 0x00,
  0x40, 0x40, 0x40, 0x40, 0x7f, 0x00, 0x00, 0x00, 0x41, 0x63, 0x55, 0x49,
  0x41, 0x00, 0x00, 0x00, 0x41, 0x61, 0x5d, 0x43, 0x41, 0x00, 0x00, 0x00,
  0x7f, 0x41, 0x41, 0x41, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x41, 0x7f, 0x40,
  0x40, 0x00, 0x00, 0x00, 0x7f, 0x41, 0x45, 0x43, 0x7f, 0x00, 0x00, 0x00,
  0x7f, 0x41, 0x7f, 0x42, 0x43, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x7f, 0x01,
  0x7f, 0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
  0x41, 0x41, 0x41, 0x41, 0x7f, 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14,
  0x08, 0x00, 0x00, 0x00, 0x41, 0x49, 0x55, 0x63, 0x41, 0x00, 0x00, 0x00,
  0x41, 0x22, 0x1c, 0x22, 0x41, 0x00, 0x00, 0x00, 0x41, 0x41, 0x7f, 0x01,
  0x7f, 0x00, 0x00, 0x00, 0x7f, 0x02, 0x1c, 0x20, 0x7f, 0x00, 0x18, 0x18,
  0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0xc0, 0xc0, 0x30, 0x30, 0xc0, 0xc0,
  0x30, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00,
  0x03, 0x3e, 0x76, 0x36, 0x36, 0x00, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07,
  0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0,
  0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
  0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
  0xc0, 0xc0, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0x03, 0x03,
  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc,
  0x33, 0x33, 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x03, 0x03,
  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18,
  0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x18, 0x18,
  0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18,
  0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
  0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
  0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
  0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0x07, 0x07,
  0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
  0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f,
  0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00,
  0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0,
  0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0xc1, 0xbe, 0xa2, 0xae, 0xa2, 0xbe,
  0xc1, 0xff, 0xc0, 0xde, 0xde, 0x80, 0x9e, 0x9e, 0x9e, 0xff, 0x81, 0xbd,
  0xbd, 0x80, 0x9e, 0x9e, 0x80, 0xff, 0xc0, 0xde, 0xdf, 0x9f, 0x9f, 0x9e,
  0x80, 0xff, 0xc0, 0xde, 0xde, 0x9e, 0x9e, 0x9e, 0x80, 0xff, 0xc0, 0xdf,
  0xdf, 0x83, 0x9f, 0x9f, 0x80, 0xff, 0xc0, 0xdf, 0xdf, 0x83, 0x9f, 0x9f,
  0x9f, 0xff, 0xc0, 0xde, 0xdf, 0x98, 0x9e, 0x9e, 0x80, 0xff, 0xde, 0xde,
  0xde, 0x80, 0x9e, 0x9e, 0x9e, 0xff, 0xfb, 0xfb, 0xfb, 0xf3, 0xf3, 0xf3,
  0xf3, 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xbc, 0xbc, 0x80, 0xff, 0xde, 0xdc,
  0xd9, 0x83, 0x99, 0x9c, 0x9e, 0xff, 0xdf, 0xdf, 0xdf, 0x9f, 0x9f, 0x9f,
  0x80, 0xff, 0x80, 0xb6, 0xb6, 0x96, 0x9e, 0x9e, 0x9e, 0xff, 0xde, 0xce,
  0xc6, 0x92, 0x98, 0x9c, 0x9e, 0xff, 0xc0, 0xdc, 0xdc, 0x9e, 0x9e, 0x9e,
  0x80, 0xff, 0xc0, 0xde, 0xde, 0x80, 0x9f, 0x9f, 0x9f, 0xff, 0xc0, 0xdc,
  0xdc, 0x9e, 0x9e, 0x9d, 0x82, 0xff, 0x81, 0xbd, 0xbd, 0x80, 0x9e, 0x9e,
  0x9e, 0xff, 0x80, 0xbe, 0xbf, 0x80, 0xfc, 0xbc, 0x80, 0xff, 0x80, 0xf7,
  0xf7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xde, 0xde, 0xde, 0x9e, 0x9e, 0x9e,
  0x80, 0xff, 0x9e, 0x9e, 0x9e, 0x9c, 0xc9, 0xe3, 0xf7, 0xff, 0xde, 0xde,
  0xde, 0x9e, 0x92, 0x80, 0x8c, 0xff, 0xbe, 0xdc, 0xe9, 0xe3, 0xcb, 0x9d,
  0xbe, 0xff, 0x9e, 0x9e, 0x9e, 0x80, 0xfe, 0xfe, 0x80, 0xff, 0x80, 0xfc,
  0xfc, 0xe3, 0x9f, 0x9f, 0x80, 0xff, 0xc3, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
  0xc3, 0xff, 0xf3, 0xed, 0xcf, 0x83, 0xcf, 0x9d, 0x03, 0xff, 0xc3, 0xf3,
  0xf3, 0xf3, 0xf3, 0xf3, 0xc3, 0xff, 0xff, 0xe7, 0xc3, 0x81, 0xe7, 0xe7,
  0xe7, 0xe7, 0xff, 0xef, 0xcf, 0x80, 0x80, 0xcf, 0xef, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xe7, 0xe7, 0xff,
  0xe7, 0xff, 0x9c, 0x9c, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x80,
  0xc9, 0xc9, 0xc9, 0x80, 0xc9, 0xff, 0xf7, 0x80, 0x97, 0x80, 0xf4, 0x80,
  0xf7, 0xff, 0x9e, 0x9e, 0xfd, 0xe3, 0xdf, 0xbc, 0xbc, 0xff, 0xc3, 0x99,
  0xc3, 0xc7, 0x98, 0x99, 0xc0, 0xff, 0xf3, 0xf3, 0xf3, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xf3, 0xe7, 0xcf, 0xcf, 0xcf, 0xe7, 0xf3, 0xff, 0xcf, 0xe7,
  0xf3, 0xf3, 0xf3, 0xe7, 0xcf, 0xff, 0xf7, 0xd5, 0xe3, 0x80, 0xe3, 0xd5,
  0xf7, 0xff, 0xf7, 0xf7, 0xf7, 0x80, 0xf7, 0xf7, 0xf7, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xe7, 0xe7, 0xc7, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xff, 0xff, 0xfc,
  0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0xff, 0x80, 0x80, 0x9c, 0x9c, 0x9c, 0x80,
  0x80, 0xff, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xff, 0x80, 0x80,
  0xfc, 0x80, 0x9f, 0x80, 0x80, 0xff, 0x80, 0x80, 0xfc, 0xf0, 0xfc, 0x80,
  0x80, 0xff, 0x9c, 0x9c, 0x9c, 0x80, 0xfc, 0xfc, 0xfc, 0xff, 0x80, 0x80,
  0x9f, 0x80, 0xfc, 0x80, 0x80, 0xff, 0x80, 0x80, 0x9f, 0x80, 0x9c, 0x80,
  0x80, 0xff, 0x80, 0x80, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0x80, 0x80,
  0x9c, 0x80, 0x9c, 0x80, 0x80, 0xff, 0x80, 0x80, 0x9c, 0x80, 0xfc, 0x80,
  0x80, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff,
  0xe7, 0xff, 0xff, 0xe7, 0xe7, 0xc7, 0xf1, 0xe7, 0xcf, 0x9f, 0xcf, 0xe7,
  0xf1, 0xff, 0xff, 0x80, 0x80, 0xff, 0x80, 0x80, 0xff, 0xff, 0x8f, 0xe7,
  0xf3, 0xf9, 0xf3, 0xe7, 0x8f, 0xff, 0x80, 0x9c, 0xfc, 0xe0, 0xff, 0xe7,
  0xe7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
  0x80, 0xbe, 0x80, 0xbe, 0xbe, 0xff, 0xff, 0xff, 0x81, 0xbd, 0x80, 0xbe,
  0x80, 0xff, 0xff, 0xff, 0x80, 0xbf, 0xbf, 0xbf, 0x80, 0xff, 0xff, 0xff,
  0x81, 0xbe, 0xbe, 0xbe, 0x81, 0xff, 0xff, 0xff, 0x80, 0xbf, 0x87, 0xbf,
  0x80, 0xff, 0xff, 0xff, 0x80, 0xbf, 0x87, 0xbf, 0xbf, 0xff, 0xff, 0xff,
  0x80, 0xbf, 0xb0, 0xbe, 0x80, 0xff, 0xff, 0xff, 0xbe, 0xbe, 0x80, 0xbe,
  0xbe, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xff, 0xff, 0xff,
  0xfe, 0xfe, 0xfe, 0xbe, 0x80, 0xff, 0xff, 0xff, 0xbe, 0xbd, 0x83, 0xbd,
  0xbe, 0xff, 0xff, 0xff, 0xbf, 0xbf, 0xbf, 0xbf, 0x80, 0xff, 0xff, 0xff,
  0xbe, 0x9c, 0xaa, 0xb6, 0xbe, 0xff, 0xff, 0xff, 0xbe, 0x9e, 0xa2, 0xbc,
  0xbe, 0xff, 0xff, 0xff, 0x80, 0xbe, 0xbe, 0xbe, 0x80, 0xff, 0xff, 0xff,
  0x80, 0xbe, 0x80, 0xbf, 0xbf, 0xff, 0xff, 0xff, 0x80, 0xbe, 0xba, 0xbc,
  0x80, 0xff, 0xff, 0xff, 0x80, 0xbe, 0x80, 0xbd, 0xbc, 0xff, 0xff, 0xff,
  0x80, 0xbf, 0x80, 0xfe, 0x80, 0xff, 0xff, 0xff, 0x80, 0xf7, 0xf7, 0xf7,
  0xf7, 0xff, 0xff, 0xff, 0xbe, 0xbe, 0xbe, 0xbe, 0x80, 0xff, 0xff, 0xff,
  0xbe, 0xbe, 0xdd, 0xeb, 0xf7, 0xff, 0xff, 0xff, 0xbe, 0xb6, 0xaa, 0x9c,
  0xbe, 0xff, 0xff, 0xff, 0xbe, 0xdd, 0xe3, 0xdd, 0xbe, 0xff, 0xff, 0xff,
  0xbe, 0xbe, 0x80, 0xfe, 0x80, 0xff, 0xff, 0xff, 0x80, 0xfd, 0xe3, 0xdf,
  0x80, 0xff, 0xe7, 0xe7, 0xe7, 0x00, 0x00, 0xe7, 0xe7, 0xe7, 0x3f, 0x3f,
  0xcf, 0xcf, 0x3f, 0x3f, 0xcf, 0xcf, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
  0xe7, 0xe7, 0xff, 0xff, 0xfc, 0xc1, 0x89, 0xc9, 0xc9, 0xff, 0x00, 0x80,
  0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff,
  0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f,
  0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33,
  0xcc, 0xcc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff,
  0xff, 0xff, 0x33, 0x33, 0xcc, 0xcc, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f,
  0x3f, 0x7f, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xe7, 0xe7,
  0xe7, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0,
  0xf0, 0xf0, 0xe7, 0xe7, 0xe7, 0xe0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0x07, 0x07, 0xe7, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
  0xe7, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xe7,
  0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0x07, 0x07, 0xe7, 0xe7, 0xe7, 0x3f, 0x3f,
  0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
  0x1f, 0x1f, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xfc,
  0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f,
  0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7,
  0xe7, 0x07, 0x07, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff,
  0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xff
};
unsigned int scrap_writer_iii_17_64c_len = 2050;


/*! \brief Perform one of the tests.
 *
 * For debugging only!
 *
 * \return 0 = no valid test found, otherwise 1
 */
int perform_a_sekrit_test(char *arg) {
  SDL_Event event;
  int running = 1;

  printf("Looking for test '%s'...\n", arg);
  movementrate = 1.0;
  if(strcmp(arg, "laserpointer") == 0) {
    float positions[10];
    float speeds[10];
    int i;
    int ysize2 = ysize / 2 - 3;

    for(i = 0; i < 10; ++i) {
      speeds[i] = rnd() / 100;
      positions[i] = 0;
    }
    
    while(running) {
      if(SDL_PollEvent(&event) != 0 && event.type == SDL_MOUSEBUTTONDOWN) running = 0;
      SDL_FillRect(surf_screen, NULL, SDL_MapRGB(surf_screen->format, 0, 0, 0x13));
      for(i = 0; i < 10; ++i) {
	draw_random_dots(surf_screen, sinf(positions[i]) * ysize2 + ysize2 + 1, 0, xsize);
	positions[i] += speeds[i];
      }
      SDL_Flip(surf_screen);
      SDL_Delay(60);
    }
    return 1;
  } else if(strcmp(arg, "charset") == 0) {
    SDL_Surface *surf;
    int i, j;
    SDL_Rect srect = { 0, 0, 8, 8 };
    SDL_Rect drect = { 0, 100, 8, 8 };
    SDL_Color colors[2] = {
      { 0x5b, 0xb, 0x5 },
      { 0xcd, 0xad, 0x32}
    };
    char msg[] = "Hello, this is really nice! !\"#$%&/()=? []{},.;:-_@^~+*'";

    if((surf = SDL_CreateRGBSurface(SDL_HWSURFACE, 8, 2050, 8, 0, 0, 0, 0)) == NULL) return 0;
    SDL_LockSurface(surf);
    SDL_SetColors(surf, colors, 0, 2);
    unsigned char *pixels = surf->pixels;
    for(i = 2; i < scrap_writer_iii_17_64c_len; ++i) {
      unsigned char ch = scrap_writer_iii_17_64c[i];
      for(j = 0; j < 8; ++j) {
	pixels[(i - 2) * surf->pitch + j] = ch & (1 << (7 - j)) ? 1 : 0;
      }
    }
    SDL_UnlockSurface(surf);
    SDL_BlitSurface(surf, NULL, surf_screen, NULL);
    for(i = 0; msg[i]; ++i) {
      char ch = msg[i];
      if(ch >= 0x40 && ch <= 0x5f) ch -= 0x40;
      else if(ch >= 0x60 && ch <= 0x7f) ch -= 0x20;
      srect.y = ch * 8;
      drect.x = 20 + i * 8;
      SDL_BlitSurface(surf, &srect, surf_screen, &drect);
    } 
    SDL_Flip(surf_screen);
    while(running) {
      if(SDL_WaitEvent(&event) != 0 && event.type == SDL_MOUSEBUTTONDOWN) running = 0;
    }
    SDL_Delay(137);
    return 1;
  } else if(strcmp(arg, "key_lab") ==0) {
    unsigned int event_nr = 0, k;
    unsigned int row = 0;
    char buf[30][128];
    SDL_Rect rect = { 0, 0, surf_screen->w, 20 };

    restart_game();
    assert(surf_screen != NULL);
    assert(surf_green_block != NULL);
    memset(buf, 0, sizeof(buf));
    while(running) {
      while(SDL_PollEvent(&event) == 1) {
	row = event_nr % 30;
	switch(event.type) {
	case SDL_QUIT:
	  running = 0;
	  break;
	case SDL_MOUSEBUTTONDOWN:
	case SDL_MOUSEBUTTONUP:
	  sprintf(buf[row], "button=%d state=%d x=$%04hX y=$%04hX", event.button.button, event.button.state, event.button.x, event.button.y);
	  break;
	case SDL_KEYDOWN:
	case SDL_KEYUP:
	  sprintf(buf[row], "state=%d sym=$%04X", event.key.state, event.key.keysym.sym);
	  break;
	case SDL_MOUSEMOTION:
	  //Ignore this one...
	  --event_nr;
	  break;
	default:
	  sprintf(buf[row], "event_nr = $%x, event.type = $%x", event_nr, (unsigned int)event.type);
	}
	++event_nr;
      }
      SDL_FillRect(surf_screen, NULL, SDL_MapRGB(surf_screen->format, 0xf, 0, 0xf));
      SDL_SetAlpha(surf_green_block, SDL_SRCALPHA, (int) (129 + 66 * sinf(fadetimer)));
      rect.y = row * 20;
      SDL_BlitSurface(surf_green_block, NULL, surf_screen, &rect);
      for(k = 0; k < 30; ++k) {
	PutString(surf_screen, 12, k * 20, buf[k]);
      }
      SDL_Flip(surf_screen);
      SDL_Delay(50);
      fadetimer += .192;
    }
    return 1;
  }
  puts("Not found!");
  return 0;
}


int restart_game() {
  int i;
  const char *txts[] = {
    "Congratulations! You have a high score.",
    "We have a winner!",
    "A place in the hall of fame has been reserved for you!",
    "Amazing performance! You earned a high score."
  };
  const int num_of_txts = sizeof(txts) / sizeof(const char *);
  char buf[256];

  cleanup_globals();
  destroy_space_dots(current_spacedot_engine);
  NULL_ERROR(current_spacedot_engine = init_space_dots_engine(surf_screen, opt_cicada_spacedots));
  if(surf_green_block) SDL_FreeSurface(surf_green_block);
  surf_green_block = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA, surf_screen->w, 22, 32, surf_screen->format->Rmask, surf_screen->format->Gmask, surf_screen->format->Bmask, surf_screen->format->Amask);
  NULL_ERROR(surf_green_block);
  SDL_FillRect(surf_green_block, NULL, SDL_MapRGB(surf_screen->format, 0, 0xa8, 0x1c));
  if(highscore_scroller) destroy_scroller(highscore_scroller);
  strcat(strcpy(buf, txts[SDL_GetTicks() % num_of_txts]), " Please enter your name using the keyboard and press enter!");
  NULL_ERROR(highscore_scroller = init_scroller(buf,
						14 * font_height + 110,
						8,
						surf_screen->w));

  for(i = 0; i < MAX_ROCKS; i++)
    rock[i].active = 0;
  rockspeed = 5.0 * xsize / 640;
  deactivate_greeblies();
  return 0;
}

int main(int argc, char **argv) {
  int i, x;
  int force_intro = 0;

  assert(RD_VIDEO_BPP == 8 * sizeof(RD_VIDEO_TYPE));
  oss_sound_flag = 1;
  load_setup();
  while((x = getopt(argc, argv, "Ifwskhx:y:")) >= 0)
    switch (x) {
    case 'I':
      force_intro = 1;
      break;
    case 'x':
      xsize = atoi(optarg);
      printf("xsize %d\n", xsize);
      break;
    case 'y':
      ysize = atoi(optarg);
      printf("ysize %d\n", ysize);
      break;
    case 's':
      oss_sound_flag = 0;
      break;
    case 'k':
      joystick_flag = 0;
      break;
    case 'f':
      opt_fullscreen = 1;
      break;
    case 'w':
      opt_fullscreen = 0;
      break;
    case 'h':
      printf("Rock Dodgers\n"
	     "  -h This help message\n"
	     "  -f Full screen\n"
	     "  -w Window mode\n"
	     "  -x xsize\n"
	     "  -y ysize\n"
	     "  -k Keyboard only - disable joystick\n"
	     "  -s Silent mode (no OSS sound)\n");
      exit(0);
      break;
    }

  initrnd();
  if(init()) {
    printf("Cannot start: '%s'\n", initerror);
    return 1;
  }

  if((optind < argc) && (perform_a_sekrit_test(argv[optind]) != 0)) {
    //A non option argument was given!
    printf("Testing for %lu ticks.\n", (unsigned long int)SDL_GetTicks());
  } else {
    play_intro(force_intro, oss_sound_flag);
    //Sometimes the game slips directly from intro to gameplay if space was pressed.
    last_ticks = SDL_GetTicks();
    temporary_disable_key_input();
    if(oss_sound_flag) play_tune(0);
    while(1) {
      restart_game();
      i = gameloop();
      if(i == 0) {
	break;
      } else if(i == 1) {
	if(!set_video_mode()) {
	  fprintf(stderr, "Setting video mode (%d, %d) failed!\n", xsize, ysize);
	  break;
	}
	if(restart_game() != 0) {
	  fprintf(stderr, "Cannot start: '%s'\n", initerror);
	  break;
	}
	state = TITLE_PAGE;
      }
      SDL_Delay(1000);
    }
  }
  shutdown_blubats();
  SDL_Quit();
  return 0;
}
